#pragma once
#include <unordered_map>
#include <string>
#include <charconv>
template<typename key_type>
struct string_converter;

template<typename key_type>
struct string_converter{
    static std::optional<key_type> convert(std::optional<std::string_view> v){
        if(v){
            key_type result;
            auto [p, ec] = std::from_chars(v->begin(),v->end(),result);
            if(ec != std::errc()){
                throw std::bad_cast();
            }
            return std::move(result);
        }else{
            return {};
        }
    }
};
//string view
template<>
struct string_converter<std::string_view>{
    static std::optional<std::string_view> convert(std::optional<std::string_view> v){
        return std::move(v);
    }
};
template<>
struct string_converter<bool>{
    static std::optional<bool> convert(std::optional<std::string_view> v){
        if(v){
            return *v == "1";
        }else{
            return {};
        }
    }
};
template<>
struct string_converter<float>{
    static std::optional<float> convert(std::optional<std::string_view> v){
        if(v){
            return std::stof(std::string{*v});
        }else{
            return {};
        }
    }
};
template<>
struct string_converter<double>{
    static std::optional<double> convert(std::optional<std::string_view> v){
        if(v){
            return std::stod(std::string{*v});
        }else{
            return {};
        }
    }
};

class server_context{
private:
    std::unordered_map<std::string,std::string> _value_map;
    
    
    std::optional<std::string_view> get_imp(const std::string& path) const;

    // set value
    // value == false means erase
    void set_imp(const std::string& path,std::optional<std::string_view> value);
public:
    server_context(){}
    server_context(std::unordered_map<std::string,std::string> init_data):_value_map(std::move(init_data)){

    }
    server_context(server_context&& other):_value_map(std::move(other._value_map)){

    }
    void operator = (server_context&& other){
        _value_map = std::move(other._value_map);
    }
    std::unordered_map<std::string,std::string>::iterator begin(){
        return _value_map.begin();
    }
    std::unordered_map<std::string,std::string>::iterator end(){
        return _value_map.end();
    }
    
    std::unordered_map<std::string,std::string>::const_iterator begin() const{
        return _value_map.begin();
    }
    std::unordered_map<std::string,std::string>::const_iterator end() const{
        return _value_map.end();
    }
    
    void erase(const std::string& path){
        set_imp(path,{});
    }

    template<typename key_type>
    void set(const std::string& path,key_type value)
    {
        set_imp(path,std::to_string(value));
    }
    
    template<typename key_type=std::string_view>
    auto get(const std::string& path) const{
        return string_converter<key_type>::convert(get_imp(path));
    }
};

template<>
void server_context::set<const std::string&>(const std::string& path,const std::string& value)
{   
    set_imp(path,value);
}

template<>
void server_context::set<const char*>(const std::string& path,const char* value)
{   
    set_imp(path,std::string{value});
}
    
std::optional<std::string_view> server_context::get_imp(const std::string& path) const
{
    if(auto ite = _value_map.find(path);ite != _value_map.end()){
        return ite->second;
    }
    return {};
}

void server_context::set_imp(const std::string& path,std::optional<std::string_view> value)
{
    if(!value){
        _value_map.erase(path);
    }else{
        _value_map[path]=*value;
    }
}